<?php

namespace app\common\service;

/**
 * 邀请码
 * Class InviteService
 * @package app\common\service
 */
class InviteService
{
    //// 以下private参数都不能变更

    /**
     * 随机字符串 32位 26个大写字母+10个数字，去掉容易混淆的，O、0、I、1等字符就剩下32个
     * @var mixed
     */
    private $randSourceStr;

    /**
     * @var int 32进制 $randSourceStr 的长度
     */
    private $charsLength = 32;

    /**
     * 邀请码长度，可以表示353788999（3.5亿多）左右个用户
     * @var int
     */
    private $codeLength = 8;

    /**
     * 随机数据，8位数，为了将id补位
     * @var int
     */
    private $slat = 12345678;

    /**
     * $prime1 与 $charsLength 互质，可保证（$id*$prime1）% $charsLength 在 [0,$charsLength)上均匀分布
     * @var int
     */
    private $prime1 = 3;

    /**
     * $prime2 与 $codeLength 互质，可保证（$id*$prime2）% $codeLength 在 [0,$codeLength)上均匀分布
     * @var int
     */
    private $prime2 = 11;


    public function __construct()
    {
        $this->randSourceStr = config('invite')['inviteSourceStr'];
    }

    /**
     * 生成邀请码
     * @param $uid // user id
     * @return bool|string
     */
    function inviteGen($uid) {
        $prime1 = $this->prime1;
        $prime2 = $this->prime2;
        $slat = $this->slat;
        $codeLength = $this->codeLength;
        $charsLength = $this->charsLength;
        $randSourceStr = $this->randSourceStr;

        // 判断是否全是数字
        if (!preg_match("/^[0-9]*$/" , $uid)){
            return false;
        }

        // 补位
        $uid = $uid * $prime1 + $slat;

        // 将id转换为32位进制
        $bArr = [];
        $bArr[0] = $uid;
        for ($i = 0; $i < $codeLength - 1; $i++) {
            $bArr[$i + 1] = $bArr[$i] / $charsLength;
            // 按位扩散
            $bArr[$i] = ($bArr[$i] + $bArr[0] * $i) % $charsLength;
        }

        // 最后两位作校验码
        $bArr[6] = ($bArr[0] + $bArr[1] + $bArr[2]) * $prime1 % $charsLength;
        $bArr[7] = ($bArr[3] + $bArr[4] + $bArr[5]) * $prime1 % $charsLength;

        $res = '';
        // 混淆
        for ($i = 0; $i < $codeLength ; $i++) {
            $res .= $randSourceStr[$bArr[$i * $prime2 % $codeLength]];
        }

        return  $res;
    }

    /**
     * 解密邀请码
     * @param $invite
     * @return bool|float|int
     */
    function deInvite($invite) {
        $prime1 = $this->prime1;
        $prime2 = $this->prime2;
        $slat = $this->slat;
        $codeLength = $this->codeLength;
        $charsLength = $this->charsLength;
        $randSourceStr = $this->randSourceStr;

        if (strlen($invite) != $codeLength) {
            // 字符串异常
            return false;
        }

        // 将字符串还原为对应数字
        $aArr = $bArr = $cArr = [];
        for ($i = 0; $i < $codeLength; $i++) {
            $index = strpos($randSourceStr, $invite[$i]);
            if ($index === false) {
                // 异常字符串
                return false;
            }
        }

        for ($i = 0; $i < $codeLength; $i++) {
            $aArr[$i * $prime2 % $codeLength] = $i;
        }

        for ($i = 0; $i < $codeLength; $i++) {
            $cArr[$i] = $invite[$aArr[$i]];
        }

        for ($i = 0; $i < $codeLength; $i++) {
            $aArr[$i] = strpos($randSourceStr, $cArr[$i]);
        }

        $bArr[6] = ($aArr[0] + $aArr[1] + $aArr[2]) * $prime1 % $charsLength;
        $bArr[7] = ($aArr[3] + $aArr[4] + $aArr[5]) * $prime1 % $charsLength;

        // 校验
        if ($aArr[6] != $bArr[6] || $aArr[7] != $bArr[7]) {
            return false;
        }

        for ($i = $codeLength - 3; $i >= 0; $i--) {
            $bArr[$i] = ($aArr[$i] - $aArr[0] * $i + $charsLength * $i) % $charsLength;
        }

        $res = 0;
        for ($i = $codeLength - 3; $i > 0; $i--) {
            $res += $bArr[$i];
            $res *= $charsLength;
        }

        return ($res + $bArr[0] - $slat) / $prime1;
    }
}